package top.jasonlam.car.controller.user;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import top.jasonlam.car.entity.*;
import top.jasonlam.car.qo.UserPageVo;
import top.jasonlam.car.qo.UserQo;
import top.jasonlam.car.controller.Comment.CommentRepository;
import top.jasonlam.car.controller.Comment.ReplyRepository;
import top.jasonlam.car.utils.JWTUtil;
import top.jasonlam.car.utils.Res;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import top.jasonlam.car.controller.car.CarRepository;

/**
 * @author jasonlam
 */
@Validated
@RestController
public class UserController {
    public static final int USERNAME_MIN = 4;
    public static final int USERNAME_MAX = 20;

    public static final int PASSWORD_MIN = 8;
    public static final int PASSWORD_MAX = 20;

    public static final int REALNAME_MIN = 2;
    public static final int REALNAME_MAX = 20;

    public static final int PHONE_LEN = 11;

    public static final int SUCCESS_CODE = 0;
    public static final int VALIDATION_CODE = 44033;
    public static final int FAIL_CODE = 55044;

    @Resource
    UserRepository userRepository;
    @Resource
    CarRepository carRepository;
    @Resource
    CommentRepository commentRepository;
    @Resource
    ReplyRepository replyRepository;

    private Res validation(User newUser) {
        String username = newUser.getUsername();
        String password = newUser.getPassword();
        String realName = newUser.getRealName();
        String phone = newUser.getPhone();
        // 用户名检查
        if (username.length() < USERNAME_MIN || username.length() > USERNAME_MAX) {
            return Res.builder().code(VALIDATION_CODE).message("用户名长度不规范").build();
        }
        // 密码检查
        if (password.length() < PASSWORD_MIN || password.length() > PASSWORD_MAX) {
            return Res.builder().code(VALIDATION_CODE).message("密码长度太短").build();
        }

        // 真实姓名检查
        if (realName.length() < REALNAME_MIN || realName.length() > REALNAME_MAX) {
            return Res.builder().code(VALIDATION_CODE).message("真实姓名长度不规范").build();
        }

        // 电话检查
        if (phone.length() != PHONE_LEN) {
            return Res.builder().code(VALIDATION_CODE).message("电话格式不正确").build();
        }
        return Res.builder().code(SUCCESS_CODE).message("内部检查成功").build();
    }

    /**
     * 登录接口
     *
     * @param username 用户名
     * @param password 密码
     * @return Res 标准化的结果，包含code和message
     */
    @GetMapping("/auth/login")
    public Res login(@RequestParam String username, @RequestParam String password) {
        try {
            User theUser = userRepository.findById(username).orElse(null);
            if (theUser == null) {
                return Res.builder().code(44033).message("用户不存在").build();
            } else if (!theUser.getPassword().equals(password)) {
                return Res.builder().code(44033).message("密码错误").build();
            } else {
                String token = JWTUtil.login(username);
                JSONObject message = new JSONObject();
                message.put("username", theUser.getUsername());
                message.put("realName", theUser.getRealName());
                message.put("phone", theUser.getPhone());
                message.put("vip", theUser.getVip());
                message.put("pushable", theUser.getPushable());
                message.put("token", token);
                return Res.builder().code(SUCCESS_CODE).message(message.toJSONString()).build();
            }
        } catch (Exception err) {
            return Res.builder().code(55033).message("服务器错误").build();
        }
    }

    /**
     * 注册接口
     *
     * @param newUser 传入json格式的用户数据
     *                {
     *                "username": "jasonlam",
     *                "password": "password",
     *                "realName": "林家祥",
     *                "phone": "13717357911",
     *                "vip": true,
     *                "pushable": true
     *                }
     * @return Res 标准化的结果，包含code和message
     */
    @PostMapping("/auth/register")
    public Res register(@RequestBody User newUser) {
        try {
            String username = newUser.getUsername();
            String realName = newUser.getRealName();
            String phone = newUser.getPhone();
            Boolean vip = newUser.getVip() == null ? true : newUser.getVip();
            Boolean pushable = newUser.getPushable() == null ? true : newUser.getPushable();
            if (newUser.getCarSet() != null) {
                return Res.builder().code(FAIL_CODE).message("不能指定用户车辆信息").build();
            }

            User theUser = userRepository.findById(newUser.getUsername()).orElse(null);
            if (theUser != null) {
                return Res.builder().code(VALIDATION_CODE).message("用户已存在").build();
            }

            // 校验账号信息正确
            Res isLawful = validation(newUser);
            if (isLawful.getCode() == VALIDATION_CODE) {
                return isLawful;
            }

            String token = JWTUtil.login(username);
            JSONObject message = new JSONObject();
            message.put("username", username);
            message.put("realName", realName);
            message.put("phone", phone);
            message.put("vip", vip);
            message.put("pushable", pushable);
            message.put("token", token);

            userRepository.save(newUser);

            return Res.builder().code(SUCCESS_CODE).message(message.toJSONString()).build();

        } catch (Exception err) {
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }

    /**
     * 修改用户接口
     *
     * @param fixUser 传入json格式的用户数据
     *                {
     *                "username": "jasonlam",
     *                "password": "password",
     *                "realName": "林家祥",
     *                "phone": "13717357911",
     *                "vip": true,
     *                "pushable": true
     *                }
     * @return Res 标准化的结果，包含code和message
     */
    @PutMapping("/user")
    public Res fixUser(@RequestBody User fixUser) {
        try {
            // 账号不存在
            User theUser = userRepository.findById(fixUser.getUsername()).orElse(null);
            if (theUser == null) {
                return Res.builder().code(VALIDATION_CODE).message("用户不存在").build();
            }
            // 校验账号信息错误
            Res isLawful = validation(fixUser);
            if (isLawful.getCode() == VALIDATION_CODE) {
                return isLawful;
            }
            if (fixUser.getRealName() != null) {
                theUser.setRealName(fixUser.getRealName());
            }
            if (fixUser.getPushable() != null) {
                theUser.setPushable(fixUser.getPushable());
            }
            if (fixUser.getVip() != null) {
                theUser.setVip(fixUser.getVip());
            }
            if (fixUser.getPassword() != null) {
                theUser.setPassword(fixUser.getPassword());
            }
            if (fixUser.getPhone() != null) {
                theUser.setPhone(fixUser.getPhone());
            }
            userRepository.save(theUser);

            String token = JWTUtil.login(theUser.getUsername());
            JSONObject message = new JSONObject();
            message.put("username", theUser.getUsername());
            message.put("realName", theUser.getRealName());
            message.put("phone", theUser.getPhone());
            message.put("vip", theUser.getVip());
            message.put("pushable", theUser.getPushable());
            message.put("token", token);

            return Res.builder().code(SUCCESS_CODE).message(message.toJSONString()).build();

        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }

    /**
     * 删除用户接口
     *
     * @param username 传入用户名
     * @return Res 标准化的结果，包含code和message
     */
    @DeleteMapping("/user")
    public Res delUser(@RequestParam String username) {
        try {
            User theUser = userRepository.findById(username).orElse(null);
            if (theUser == null) {
                return Res.builder().code(VALIDATION_CODE).message("该用户不存在").build();
            }
            userRepository.delete(theUser);
            return Res.builder().code(SUCCESS_CODE).message("删除成功").build();

        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }

    /**
     * 动态查询   https://blog.csdn.net/liuyunyihao/article/details/81194007
     * pageSize 1~n  单页面大小
     * pageNum当前页数  1~n
     */
    @GetMapping("/userpage")
    public Res pageUser(@RequestParam int pageSize, @RequestParam int pageNum, @Nullable @RequestBody UserQo userQo) {
        try {
            Specification<User> specification = null;
            Page<User> studentEntities = null;
            Pageable pageable = PageRequest.of(pageNum - 1, pageSize);
            if (userQo != null) {
                specification = (Specification<User>) (root, query, cb) -> {
                    List<Predicate> predicates = new ArrayList<>();
                    if (userQo.getUserName() != null && !"".equals(userQo.getUserName())) {
                        predicates.add(cb.equal(root.<String>get("username"), userQo.getUserName()));
                    }
                    if (userQo.getPhone() != null && !"".equals(userQo.getPhone())) {
                        predicates.add(cb.greaterThan(root.<String>get("phone"), userQo.getPhone()));
                    }
                    if (userQo.getRealName() != null && !"".equals(userQo.getRealName())) {
                        predicates.add(cb.lessThan(root.<String>get("realName"), userQo.getRealName()));
                    }
                    return cb.and(predicates.toArray(new Predicate[predicates.size()]));
                };
                studentEntities = userRepository.findAll(specification, pageable);
            }
            if (studentEntities == null) {
                studentEntities = userRepository.findAll(pageable);
            }

            JSONObject message = new JSONObject();
            message.put("users", UserPageVo.builder().totalNum(studentEntities.getTotalElements())
                    .totalPage(studentEntities.getTotalPages()).userList(studentEntities.getContent()).build());
            return Res.builder().code(SUCCESS_CODE).message(message.toJSONString()).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }

    /**
     * 获取用户信息
     *
     * @param request header里需要存放token
     * @return
     */
    @Transactional
    @GetMapping("/user")
    public Res getUser(HttpServletRequest request) {
        try {
            String token = request.getHeader("token");
            String subject = JWTUtil.parseJWT(token).getSubject();
            System.out.println(subject);
            User u = userRepository.findById(subject).orElse(null);
            ObjectMapper objectMapper = new ObjectMapper();

            return Res.builder().code(SUCCESS_CODE).message(objectMapper.writeValueAsString(u)).build();
        } catch (ExpiredJwtException jwtException) {
            return Res.builder().code(FAIL_CODE).message("token已过期错误").build();
        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }

    /**
     * 添加/修改二手车信息
     *
     * @param car json {
     *            "uuid": "test-uuid",      //无则添加，有则修改
     *            "username": "jasonlam",
     *            "title": "ershouche",
     *            "status": "0",
     *            "price": "10000",
     *            "img": "[]",
     *            "describe": "这是一辆没人要的二手车"
     *            }
     * @return Res
     */
    @PostMapping("/user/car")
    public Res addCar(@Valid @RequestBody Car car) {
        try {

            if (car.getCommentSet() != null) {
                return Res.builder().code(FAIL_CODE).message("参数错误，不能指定车辆发布单回复信息").build();
            }
            // 如果ID非空，说明是修改
            if (car.getUuid() != null) {
                Car theCar = carRepository.findById(car.getUuid()).orElse(null);
                if (theCar == null) {
                    return Res.builder().code(FAIL_CODE).message("未找到车辆信息").build();
                } else {
                    if (car.getPrice() != null) {
                        theCar.setPrice(car.getPrice());
                    }
                    if (car.getDescribe() != null) {
                        theCar.setDescribe(car.getDescribe());
                    }
                    if (car.getStatus() != null) {
                        theCar.setStatus(car.getStatus());
                    }
                    if (car.getTitle() != null) {
                        theCar.setTitle(car.getTitle());
                    }
                    if (car.getImg() != null) {
                        theCar.setImg(car.getImg());
                    }
                    carRepository.save(theCar);
                    return Res.builder().code(SUCCESS_CODE).message(theCar.getUuid()).build();
                }
            } else {
                carRepository.save(car);
                return Res.builder().code(SUCCESS_CODE).message(car.getUuid()).build();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }

    /**
     * 用户获取单辆车辆
     * 返回的是 car 车辆发布单以及级联的评论   评论又级联了回复
     */
    @GetMapping("/user/car")
    public Res getCar(@NotEmpty(message = "Id 不能为空") @RequestParam String carId) {
        try {
            Car car = carRepository.findById(carId).orElse(null);

            if (car == null) {
                return Res.builder().code(FAIL_CODE).message("没有找到指定车辆").build();
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("car", car);
            return Res.builder().code(SUCCESS_CODE).message(jsonObject.toJSONString()).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }

    /**
     * 分页获取发布单
     *
     * @param pagesize
     * @param pagenum
     * @param username
     * @return
     */
    @GetMapping("/user/carpage")
    public Res getCarPage(@NotNull(message = "页面大小不能为空") @Min(value = 10, message = "页面大小最小为10") @RequestParam Integer pagesize,
                          @NotNull(message = "页数不能为空") @Min(value = 1, message = "页数最小为1") @RequestParam Integer pagenum,
                          @Nullable @RequestParam String username) {
        try {
            Pageable pageable = PageRequest.of(pagenum - 1, pagesize);
            Page<Car> car = null;
            if (username == null) {
                car = carRepository.findAll(pageable);
            } else {
                car = carRepository.findAllByUsername(username, pageable);
            }

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("cars", car.getContent());
            jsonObject.put("totalNum", car.getTotalElements());
            jsonObject.put("totalPage", car.getTotalPages());

            return Res.builder().code(SUCCESS_CODE).message(jsonObject.toJSONString()).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }

    @PutMapping("/user/car/comment")
    public Res setComment(@Valid @RequestBody Comment comment) {
        try {
            if (comment.getId() != null) {
                return Res.builder().code(FAIL_CODE).message("参数错误，不能指定id").build();
            }
            if (comment.getReplySet() != null) {
                return Res.builder().code(FAIL_CODE).message("参数错误，不能指定回复").build();
            }
            if (comment.getCommentDate() != null) {
                return Res.builder().code(FAIL_CODE).message("参数错误，不能指定日期").build();
            }
            User u = userRepository.findById(comment.getCommentName()).orElse(null);
            if (u == null) {
                return Res.builder().code(FAIL_CODE).message("参数错误，找不到对应用户").build();
            }
            commentRepository.save(comment);

            return Res.builder().code(SUCCESS_CODE).message("评论成功").build();
        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }

    /**
     * @param reply
     * @return
     */
    @PutMapping("/user/car/reply")
    public Res setReply(@Valid @RequestBody Reply reply) {
        try {
            if (reply.getId() != null) {
                return Res.builder().code(FAIL_CODE).message("参数错误，不能指定id").build();
            }
            if (reply.getReplyDate() != null) {
                return Res.builder().code(FAIL_CODE).message("参数错误，不能指定日期").build();
            }
            User u = userRepository.findById(reply.getCommentName()).orElse(null);
            if (u == null) {
                return Res.builder().code(FAIL_CODE).message("参数错误，找不到对应被评论人").build();
            }
            u = userRepository.findById(reply.getReplyName()).orElse(null);
            if (u == null) {
                return Res.builder().code(FAIL_CODE).message("参数错误，找不到对应评论人").build();
            }
            replyRepository.save(reply);
            return Res.builder().code(SUCCESS_CODE).message("评论成功").build();
        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(FAIL_CODE).message("服务器错误").build();
        }
    }


    /**
     * 获取发布单对应的所有评论和回复
     *
     * @param carId 发布单的id
     * @return Res
     */
    @GetMapping("/user/car/comment")
    public Res getComment(@NotEmpty String carId) {
        try {
            Optional<Car> car = carRepository.findById(carId);
            if (car.isPresent()) {
                Set<Comment> comments = car.get().getCommentSet();
                ObjectMapper objectMapper = new ObjectMapper();
                return Res.builder().code(SUCCESS_CODE).message(objectMapper.writeValueAsString(comments)).build();
            } else {
                return Res.builder().code(SUCCESS_CODE).message("没有找到车辆发布单").build();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return Res.builder().code(SUCCESS_CODE).message("服务器发生错误").build();
        }
    }

}
